package com.telenav.snowsweeper.renderer;

import java.util.Vector;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Cap;

import com.telenav.snowsweeper.data.DisplayUtil;
import com.telenav.snowsweeper.data.Edge;
import com.telenav.snowsweeper.framework.ui.IObjectRenderer;

public class MapEdgeRenderer implements IObjectRenderer {

	private RendererParams params;
	private RendererParams current;
	private MapEdgeBuilder builder;
	
	private Paint p;
	
	public MapEdgeRenderer(MapEdgeBuilder builder) {
		this.builder = builder;
		this.params = new RendererParams();
		
		p = new Paint();
		p.setColor(DisplayUtil.COLOR_VALID_EDGE);
		p.setStyle(Paint.Style.STROKE);
		p.setStrokeCap(Cap.ROUND);
	}
	
	public RendererParams getRenderParams() {
		return params;
	}

	@Override
	public void render(Canvas canvas, Layer layer) {
		if (layer == Layer.Edge) {
			if (params.dirty || current == null) {
				current = params.clone();
				builder.build(current);
			}
			
			int[] head, tail;
			long timeStamp = current.timeStamp;
			for (Edge edge : current.edgesOnScreen) {
				head = edge.getBegin().getScreenXY(timeStamp);
				tail = edge.getEnd().getScreenXY(timeStamp);
				if (head == null || tail == null)
					continue;
				drawEdge(canvas, head, tail, edge.getPosTransTime(), edge.getNegTransTime());
			}
		}
	}
	
	
	private void drawEdge(Canvas canvas, int[] start, int[] end, int posT, int negT) {
		if (start[0] == end[0]) {
			drawVEdge(canvas, start, end, posT, negT);
		}
		else {
			drawHEdge(canvas, start, end, posT, negT);
		}
	}
	
	private void drawVEdge(Canvas canvas, int[] start, int[] end, int posT, int negT) {
		int offset = (((int)p.getStrokeWidth()) >> 1) + 1;
		p.setPathEffect(posT > 0 ? null : DisplayUtil.EDGE_DASH_STYLE);
		p.setColor(posT > 0 ? DisplayUtil.COLOR_VALID_EDGE : DisplayUtil.COLOR_INVALID_EDGE);
		canvas.drawLine(start[0] + offset, start[1], end[0] + offset, end[1], p);
		p.setPathEffect(negT > 0 ? null : DisplayUtil.EDGE_DASH_STYLE);
		p.setColor(negT > 0 ? DisplayUtil.COLOR_VALID_EDGE : DisplayUtil.COLOR_INVALID_EDGE);
		canvas.drawLine(start[0] - offset, start[1], end[0] - offset, end[1], p);
	}
	
	private void drawHEdge(Canvas canvas, int[] start, int[] end, int posT, int negT) {
		int offset = (((int)p.getStrokeWidth()) >> 1) + 1;
		p.setPathEffect(posT > 0 ? null : DisplayUtil.EDGE_DASH_STYLE);
		p.setColor(posT > 0 ? DisplayUtil.COLOR_VALID_EDGE : DisplayUtil.COLOR_INVALID_EDGE);
		canvas.drawLine(start[0], start[1] - offset, end[0], end[1] - offset, p);
		p.setPathEffect(negT > 0 ? null : DisplayUtil.EDGE_DASH_STYLE);
		p.setColor(negT > 0 ? DisplayUtil.COLOR_VALID_EDGE : DisplayUtil.COLOR_INVALID_EDGE);
		canvas.drawLine(start[0], start[1] + offset, end[0], end[1] + offset, p);
	}
	
	public static class RendererParams {
		// fixed-point number, have 6 bits decimal.
		int scaleX = 64;
		int scaleY = 64;
		int centerX = 0;
		int centerY = 0;
		
		Vector<Edge> edgesOnScreen = new Vector<Edge>();
		int edgeWidth = -1;
		long timeStamp = -1;
		boolean dirty = true;
		
		public synchronized void setScaleXY(int sx, int sy) {
			this.scaleX = sx;
			this.scaleY = sy;
			dirty = true;
		}
		
		public synchronized void adjustScaleXY(int dx, int dy) {
			this.scaleX += dx;
			this.scaleY += dy;
			if (scaleX < Util.EDGE_MIN_SCALE)
				scaleX = Util.EDGE_MIN_SCALE;
			else if (scaleX > Util.EDGE_MAX_SCALE)
				scaleX = Util.EDGE_MAX_SCALE;
			if (scaleY < Util.EDGE_MIN_SCALE)
				scaleY = Util.EDGE_MIN_SCALE;
			else if (scaleY > Util.EDGE_MAX_SCALE)
				scaleY = Util.EDGE_MAX_SCALE;
			dirty = true;
		}
		
		public synchronized void move2(int cx, int cy) {
			this.centerX = cx;
			this.centerY = cy;
			dirty = true;
		}
		
		public synchronized void move(int dx_px, int dy_px) {
			dx_px = Util.screenDist2Map(dx_px, scaleX);
			dy_px = Util.screenDist2Map(dy_px, scaleY);
			this.centerX += dx_px;
			this.centerY += dy_px;
			if (centerX < Util.EDGE_MAP_MIN_X)
				centerX = Util.EDGE_MAP_MIN_X;
			else if (centerX > Util.EDGE_MAP_MAX_X)
				centerX = Util.EDGE_MAP_MAX_X;
			if (centerY < Util.EDGE_MAP_MIN_Y)
				centerY = Util.EDGE_MAP_MIN_Y;
			else if (centerY > Util.EDGE_MAP_MAX_Y)
				centerY = Util.EDGE_MAP_MAX_Y;
			dirty = true;
		}
		
		public RendererParams clone() {
			RendererParams params = new RendererParams();
			synchronized (this) {
				params.scaleX = scaleX;
				params.scaleY = scaleY;
				params.centerX = centerX;
				params.centerY = centerY;
				timeStamp = -1;
				params.dirty = true;
				dirty = false;
			}
			return params;
		}
	}
}
